﻿
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TextTemplating.VSHost;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using System.IO;
using System.CodeDom.Compiler;
using System.Reflection;
using devtm.Generator.ConfigDesigner.CustomCode.CodeGeneration;
using System.Xml.Linq;
using Microsoft.VisualStudio.Designer.Interfaces;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;
using System.CodeDom;
using System.ComponentModel.Composition;
using Microsoft.VisualStudio;
using System.Diagnostics;


namespace devtm.AutoMapper
{
    // Inform our Package that we're providing a Code Generator
    //[ProvideCodeGenerator( typeof( CsdFileGenerator ), "CsdFileGenerator",  "Generates implementations of the configuration describled in .csd files", true )]
    //internal sealed partial class ConfigurationSectionDesignerPackage
    //{
    //}

    [CLSCompliant(false)]
    [Guid("DB16D5EB-E3C0-4B5F-A9CE-9FE18F69750B")]
    public class CodeGenerator : TemplatedCodeGenerator
    {


        public Microsoft.VisualStudio.Shell.Interop.IVsGeneratorProgress _generateProgress;

        protected Microsoft.VisualStudio.Shell.Interop.IVsGeneratorProgress GenerateProgress
        {
            get
            {
                if (_generateProgress == null)
                    _generateProgress = GetService(typeof(Microsoft.VisualStudio.Shell.Interop.IVsGeneratorProgress)) as Microsoft.VisualStudio.Shell.Interop.IVsGeneratorProgress;
                    
                return _generateProgress;
            }
        }



       

        private CodeDomProvider codeDomProvider;

        /// <summary>
        /// Returns a CodeDomProvider object for the language of the project containing
        /// the project item the generator was called on
        /// </summary>
        /// <returns>A CodeDomProvider object</returns>
        protected CodeDomProvider CodeProvider
        {
            get
            {
                if (codeDomProvider == null)
                {
                    //Query for IVSMDCodeDomProvider/SVSMDCodeDomProvider for this project type
                    IVSMDCodeDomProvider provider = SiteServiceProvider.GetService(typeof(SVSMDCodeDomProvider)) as IVSMDCodeDomProvider;
                    if (provider != null)
                        return codeDomProvider = provider.CodeDomProvider as CodeDomProvider;
                    else
                        //In the case where no language specific CodeDom is available, fall back to C#
                        return codeDomProvider = CodeDomProvider.CreateProvider("C#");
                }
                return codeDomProvider;
            }
        }



        protected override byte[] GenerateCode(string inputFileName, string inputFileContent)
        {

           
            var ii = GenerateProgress;
            //CSharpParser.SCharpType.Parser.InCache = true;

            
            devtm.AutoMapper.Helpers._output.WriteLine("Start automapper generation");
            try
            {

                using (Store store = new Store(typeof(CoreDesignSurfaceDomainModel), typeof(AutoMapperDomainModel)))
                {

                    Stopwatch _time = new Stopwatch();
                    _time.Start();

                    MappingModel model = PrepareModel(inputFileName, store);
                    CodeCompileUnit generationUnit = PrepareCodeGenerator();

                    string AutoMapperName = !string.IsNullOrEmpty(model.ClassName) ? model.ClassName : "AutoMapper";

                    AutoMapperCodeDomGenerator gen = new AutoMapperCodeDomGenerator(AutoMapperName);
                    _time.Stop();
                    devtm.AutoMapper.Helpers._output.WriteLine(string.Format("Generated Code in {0}", _time.Elapsed));

                    _time.Start();
                    generationUnit = gen.Generate(generationUnit, model);
                    _time.Stop();


                    _generator.GenerateCodeFromCompileUnit(generationUnit, _codeWriter, _options);

                    devtm.AutoMapper.Helpers._output.WriteLine(string.Format("Writed in {0}", _time.Elapsed));

                    _codeWriter.Flush();
                    byte[] output = new byte[_codeStream.Length];
                    Array.Copy(_codeStream.GetBuffer(), 0, output, 0, _codeStream.Length);
                    return output;


                }

            }
            catch (Exception ex)
            {

                devtm.AutoMapper.Helpers._output.WriteLine(ex.ToString());              

                return null;
                throw;
            }
            finally
            {
                ContainerTypes.ClearCache();
                //CSharpParser.SCharpType.Parser.InCache = false;

                devtm.AutoMapper.Helpers._output.WriteLine("Stop automapper generation");

            }

        }


        private static MappingModel PrepareModel(string inputFilePath, Store store)
        {
            MappingModel model;

            using (Transaction transaction = store.TransactionManager.BeginTransaction("Load", true))
            {
                SerializationResult serializationResult = new SerializationResult();
                model = AutoMapperSerializationHelper.Instance.LoadModel(serializationResult, store, inputFilePath, null, null, null);
                model.FilePath = inputFilePath;

                if (serializationResult.Failed)
                    throw new SerializationException(serializationResult);
                
                transaction.Commit();
            }
            return model;
        }

        private CodeCompileUnit PrepareCodeGenerator()
        {
            _codeStream = new MemoryStream();
            _codeWriter = new StreamWriter(_codeStream);
            _generator = CodeProvider.CreateGenerator(_codeWriter);
            CodeCompileUnit generationUnit = new CodeCompileUnit();
            return generationUnit;
        }

        private MemoryStream _codeStream;
        private StreamWriter _codeWriter;
        private ICodeGenerator _generator;

        private CodeGeneratorOptions _options = new CodeGeneratorOptions
        {
            BlankLinesBetweenMembers = true,
            BracingStyle = "C",
            VerbatimOrder = true
        };


    }
}
